package com.hoshino.momochi.config.Trie;

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Future;

@Component
public class Trie {

    // trie树的根节点
    private TreeNode root;

    // trie树中拥有多少分枝（多少个敏感词）
    private int size;

    public Trie() {
        this.root = new TreeNode();
        this.size = 0;
    }

    /**
     * @Description: 返回trie树中分枝树（敏感词树）
     */
    public int getSize() {
        return size;
    }


    /**
     * @param word 添加的敏感词
     */
    public void addWord(String word) {
        // 设置当前节点为根节点
        TreeNode cur = root;
        char[] words = word.trim().toCharArray();

        for (char c : words) {
            // 排除空格
            if (Character.isWhitespace(c)) {
                continue;
            }
            // 判断当前节点的子节点中是否存在字符c
            if (!cur.children.containsKey(c)) {
                // 如果不存在则将其添加进行子节点中
                cur.children.put(c, new TreeNode());
            }
            // 当前节点进行变换，变换为新插入到节点 c
            cur = cur.children.get(c);
        }
        // 分枝添加完成后，将分枝中的最后一个节点设置为叶子节点
        if (!cur.isWord) {
            cur.isWord = true;
            // 分枝数（敏感词数）加1
            size++;
        }
    }


    /**
     * 查询字典树中是否有 word
     *
     * @param word
     * @return
     */
    public boolean isContains(String word) {
        TreeNode cur = root;
        char[] words = word.toCharArray();

        for (char c : words) {
            if (!cur.children.containsKey(c)) {
                return false;
            }
            cur = cur.children.get(c);
        }

        return cur.isWord;
    }


    /**
     * 敏感词替换
     *
     * @param word 一段话，如果有敏感词需要被替换的词
     * @return
     */
    public String sensitiveWordReplace(String word) {
        System.out.println("敏感词替换前：" + word);

        TreeNode cur = root;
        char[] words = word.toCharArray();
        // 需要被替换的敏感词
        StringBuilder oldTemp = new StringBuilder();
        // 需要替换成的星号
        StringBuilder starTemp = new StringBuilder();

        for (char c : words) {
            if (!cur.children.containsKey(c)) {
                // 判断当前字符为 空格、tab 键等
                if (Character.isWhitespace(c) && oldTemp.length() > 0) {
                    oldTemp.append(c);
                    starTemp.append("*");
                }
                // 如果当前节点的孩子节点中没有此单词则直接跳过此循环，进入下次循环
                continue;
            }
            if (!cur.isWord) {
                // 拼接上word和trie树都有的字符
                oldTemp.append(c);
                starTemp.append("*");
                cur = cur.children.get(c);
            }
            if (cur.isWord) {
                // 进行敏感词替换
                word = word.replaceAll(oldTemp.toString(), starTemp.toString());
                // 清空StringBuilder中内容
                oldTemp.delete(0, oldTemp.length());
                starTemp.delete(0, starTemp.length());
                // 查找一个敏感词并替换后，需要重新从根节点进行遍历，所以当前节点指向root
                cur = root;
            }
        }
        System.out.println("敏感词替换后：" + word);
        return word;
    }

    @Async
    public Future<String> haveAsyncSensitiveWordReplace(String word) {
        return new AsyncResult<>(sensitiveWordReplace(word));
    }

    public String synchronizeSensitiveWordReplace(String word) {
        return sensitiveWordReplace(word);
    }


    /**
     * 存放trie树中查询到的联想词
     */
    private List<String> list = new ArrayList<String>();

    /**
     * @param word
     * @Description: 利用trie的公共前缀特性，可以实现关键词自动联想
     */
    public void prefixMatching(String word, TreeNode root) {
        TreeNode cur = root;
        char[] words = word.trim().toCharArray();
        StringBuilder str = new StringBuilder();
        str.append(word);
        // 获取关键词最后一个节点
        for (int i = 0; i < words.length; i++) {
            if (Character.isWhitespace(words[i])) {
                continue;
            }
            if (!cur.children.containsKey(words[i])) {
                System.out.println("无关联词！");
                return;
            }
            cur = cur.children.get(words[i]);
        }
        // 根据获取到的关键词的最后节点查询后面所有的符合的联想词
        dfs(str, cur);
        System.out.println("[ " + word + " ]在trie树中的联想词：" + Arrays.toString(list.toArray()));
    }


    /**
     * @param word 需要查找的关键词
     * @param root 关键词的最后一个词在trie上的节点
     * @Description: 节点遍历
     */
    public void dfs(StringBuilder word, TreeNode root) {
        TreeNode cur = root;
        if (cur.isWord) {
            list.add(word.toString());
            if (cur.children.size() == 0) {
                return;
            }
        }
        for (Character s : cur.children.keySet()) {
            // 如果字符是空格或者tab键等
            if (Character.isWhitespace(s)) {
                continue;
            }
            word.append(s);
            // 递归调用
            dfs(word, cur.children.get(s));
            word.delete(word.length() - 1, word.length());
        }
    }


}
